package com.grass.plugin.escape.ibatis;

import java.lang.reflect.Field;
import java.util.Properties;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

import com.grass.plugin.DialectFactory;


/**
 * @author wujx
 * QQ 396246973
 * Date 2013-02-10
 * ibatis 拦截器用于实现定界符转义
 * 注意 转义拦截器仅适合声明式SQL语句，嵌入式的SQL慎用
 */
@Intercepts({@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
	@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})})
//@Intercepts(@Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class }))
public class EscapePlugin implements Interceptor {

	private String dialect;
	private String lmark = "[";
	private String rmark = "]";
	private String format = "upper";//upper or lower or none
	/**
	 * 设置方言类型
	 * @param dialect
	 */
	public void setDialect(String dialect) {
		this.dialect = dialect;
	}
	/**
	 * 设置定界符左侧类型 默认#
	 * @param lmark
	 */
	public void setLmark(String lmark) {
		this.lmark = lmark;
	}
	/**
	 * 设置定界符有侧类型 默认@
	 * @param rmark
	 */
	public void setRmark(String rmark) {
		this.rmark = rmark;
	}
	/**
	 * 设置Sql格式化类型 none保持原始格式 upper全部大 lower全部小写 默认none
	 * @param format
	 */
	public void setFormat(String format) {
		this.format = format;
	}

	public Object intercept(Invocation invocation) throws Throwable {
//		StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
//		BoundSql boundSql = statementHandler.getBoundSql();
//		String sql = boundSql.getSql().trim().replaceAll("[\r\n]", " ").replaceAll("\\s{2,}", " ");
//		MetaObject metaStatementHandler = MetaObject.forObject(statementHandler);
//		metaStatementHandler.setValue("delegate.boundSql.sql",DialectFactory.getDialectByDbtype(dialect).Escape(sql,lmark,rmark));		
//		return invocation.proceed();
		MappedStatement ms = (MappedStatement) invocation.getArgs()[0];
		Object parameter = invocation.getArgs()[1];		
		BoundSql boundSql = ms.getBoundSql(parameter);

		String sql = boundSql.getSql().trim().replaceAll("[\r\n]", " ").replaceAll("\\s{2,}", " ");
		if("upper".equalsIgnoreCase(format)){
			sql = sql.toUpperCase();
		}else if("lower".equalsIgnoreCase(format)){
			sql = sql.toLowerCase();
		}
		Field field = boundSql.getClass().getDeclaredField("sql");
        field.setAccessible(true);
        field.set(boundSql, DialectFactory.getDialectByDbtype(dialect).Escape(sql,"\\" + lmark,"\\" + rmark));
        MappedStatement.Builder builder = new MappedStatement.Builder(ms.getConfiguration(), ms.getId(), new BoundSqlSqlSource(boundSql), ms.getSqlCommandType());
        builder.resource(ms.getResource());
        builder.fetchSize(ms.getFetchSize());
        builder.statementType(ms.getStatementType());
        builder.keyGenerator(ms.getKeyGenerator());
        builder.cache(ms.getCache());
        builder.useCache(ms.isUseCache());
        builder.timeout(ms.getTimeout());
        builder.resultSetType(ms.getResultSetType());
        builder.parameterMap(ms.getParameterMap());
        builder.resultMaps(ms.getResultMaps());
        builder.flushCacheRequired(ms.isFlushCacheRequired());        
        builder.resultOrdered(ms.isResultOrdered());
        StringBuilder str = new StringBuilder();
        if(null!=ms.getKeyProperties())
            for(String n:ms.getKeyProperties())
        	    str.append(",").append(n);
        builder.keyProperty(str.toString().replaceFirst(",",""));        
        str.setLength(0);
        if(null!=ms.getKeyColumns())
	        for(String n:ms.getKeyColumns())
	        	str.append(",").append(n);
        builder.keyColumn(str.toString().replaceFirst(",",""));
        builder.databaseId(ms.getDatabaseId());
        builder.lang(ms.getLang());
        
        String Propertys = "";
        if(ms.getKeyProperties()!=null){
        	for(String Property : ms.getKeyProperties())
        		Propertys += Property + ",";
        	
        	if(Propertys.length() > 1)
        		Propertys.substring(0, Propertys.length() - 1);
        }
        builder.keyProperty(Propertys);
        builder.timeout(ms.getTimeout());
        builder.parameterMap(ms.getParameterMap());
        builder.resultMaps(ms.getResultMaps());
        builder.cache(ms.getCache());
        invocation.getArgs()[0] = builder.build();
		return invocation.proceed();
	}

	public Object plugin(Object target) {
		return Plugin.wrap(target, this);
	}

	public void setProperties(Properties properties) {
	}
	
    private class BoundSqlSqlSource implements SqlSource {

        BoundSql boundSql;

        public BoundSqlSqlSource(BoundSql boundSql) {
            this.boundSql = boundSql;
        }

        public BoundSql getBoundSql(Object parameterObject) {
            return boundSql;
        }
    }
}